<!DOCTYPE html>
<html>

<head>
  <meta charset="utf-8" />
  <title>文档标题</title>
  <meta name="viewport" content="width=device-width, initial-scale=1, maximum-scale=1">
  <!--link rel="stylesheet" type="text/css" href="https://www.layuicdn.com/layui-v2.6.8/css/layui.css" /-->
  <link rel="stylesheet" type="text/css" href="css/layui.css" />
  <style>
    .grid-demo {
      padding: 10px;
      line-height: 50px;
      text-align: center;
      background-color: #79c48c;
      color: #fff;
    }
    
    .grid-demo-bg1 {
      background-color: #63ba79;
    }
    
    .grid-demo-bg2 {
      background-color: #49a761;
    }
    
    .grid-demo-bg3 {
      background-color: #38814a;
    }
  </style>
</head>

<body>
  <!--script src="https://www.layuicdn.com/layui-v2.6.8/layui.js"></script-->
  <script src="layui.js"></script>
  <!--您的Layui代码start-->
  <script src="data.js?" + new date().getTime()></script>
  <script type="text/javascript">
  var person = {
    // --- 核心属性 ---
    vitality: 10,       // 体质
    intelligent: 5,     // 智力
    luck: 5,            // 运气
    age: 0,             // 年龄
    gold: 100,          // 金钱
    ageLimit: 80,       // 寿命
    isDead: 0,          // 是否死亡 (0为活，1为死)
    
    // --- 状态与关系 ---
    family: 0,          // 家庭背景
    happy: 5,           // 开心程度
    social: 3,          // 社交能力
    appearance: 5,      // 颜值
    has_partner: 0,     // 是否有伴侣 (0:无, 1:有)
    has_child: 0,       // 是否有孩子 (0:无, 1:有)
    
    // --- 教育与工作 ---
    hasElementary: 0,   // 是否上过小学
    education: 0,       // 学历 (0:无, 1:高中, 2:大学, 3:名牌大学)
    major_type: 0,      // 专业类型 (1:理科, 2:文科)
    job_prospects: 0,   // 工作前景
    work_intensity: 0,  // 工作强度
    
    // --- 财务 ---
    debt: 0             // 负债
  };

    /*
     * 随机打散数组，效率比较高
     * parameters:
     * array, 数组
     * len, 随机后的数组长度，默认与原数组一样
     * return:
     * 数组
     */
    function shuffle(array, len = array.length) {
      var result = [];
      for (var i = 0; i < len; i++) {
        var ran = Math.floor(Math.random() * (array.length - i));
        result.push(array[ran]);
        array[ran] = array[array.length - i - 1];
      };
      return result;
    }

    /* 
     * 对字符串分段加上前缀之后再或执行
     * 比如"str > 3"会变成 "person.str > 3"，再执行，得一个布尔值
     * "age < ageLimit"会变成 "person.age < person.ageLimit"，再执行得布尔值
     * "gold += 50"会变成"person.gold += 50"，再执行，累加完成
     */
    function x_eval(str, prefix = "person") {
      // 先处理 "A 操作符 B" 的简单格式
      const parts = str.split(" ");
      if (parts.length === 3) {
        const [left, op, right] = parts;
        const evalLeft = (person.hasOwnProperty(left)) ? `${prefix}.${left}` : left;
        const evalRight = (person.hasOwnProperty(right)) ? `${prefix}.${right}` : right;
        try {
          return eval(`${evalLeft} ${op} ${evalRight}`);
        } catch (e) {
          console.error(`x_eval在执行简单表达式时出错: "${str}" -> "${evalLeft} ${op} ${evalRight}"`, e);
          return false;
        }
      }

      // 再处理复杂表达式
      // 这个正则表达式会匹配所有可能是变量名的东西
      let dynamicStr = str.replace(/[a-zA-Z_][a-zA-Z0-9_]*/g, (match) => {
        // 只有当 person 对象里确实有这个属性时，我们才添加前缀
        if (person.hasOwnProperty(match)) {
          return `${prefix}.${match}`;
        }
        // 否则，保持原样（比如 true, false, Math, isNaN 等关键字或函数）
        return match;
      });

      try {
        return eval(dynamicStr);
      } catch (e) {
        console.error(`x_eval在执行复杂表达式时出错: "${str}" -> "${dynamicStr}"`, e);
        // 在复杂表达式计算失败时，返回一个极小值或0，避免影响权重计算
        return 0; 
      }
    }

    /*
     * 数组元素可能是三种情况：数字、字符串A型（含“ == ”“ >= ”等比较符，要点是要用空格隔开）、字符串B型（无空格，视为取人物的该属性直接作为权重）
     * 其中A型字符串比较特殊，一旦为真，则此项必中；一旦为假，则此项必不中。
     * 一个数组中不允许含有多个A型字符串。------------这一点有点疑问，因为有“七岁还没上学则必须上学”这种按年龄强制事件的出现，使我不得不考虑把卡片的权重也变成A型，
     * 但这样一来，说不定就会有多个A型的权重值同时出现……目前先只考虑“某年龄必须做某事”这种情况吧，就不会出现多个A型同时出现了。
     * B型字符串可以乘以一个系数（但不可以除。可以把其它的乘以系数来变向达到除的目的）来算权重。但要注意先写属性后写系数，且中间不要空格。
     */
    function new_weight_randomize(weighted_array, prefix = "person") {
      var result_index;
      // 先判断每一项是否为A型字符串，有且求值为真则标记之，有且求值为假则记为0（不参与加权）；
      // 同时也把B型字符串的值求出来
      var _array = [];
      weighted_array.forEach(function(item, index) {
        if (typeof(item) == "string" && item.split(" ").length > 1) {
          if (x_eval(item, prefix)) {
            result_index = index;
          } else {
            _array.push(0);
          }
        } else {
          let weight_value;
          if (typeof(item) == "string") {
            weight_value = x_eval(item, prefix);
          } else {
            weight_value = item
          }
          _array.push(weight_value);
        }
      });
      // 对全塌陷成数字的数组再来加权计算
      if (typeof(result_index) == "undefined") {
        result_index = get_weighted_rand_index(_array);
      }
      return result_index;
    }

    /* 
     * 对数组加权随机的“核心”算法。
     * parameters:''
     * weighted_array, 权重数组。比如[1, 200]，表示一个权重是1，一个权重是200。那么随机的时候，就有1/201的几率随机到第一项(index = 0)，200/201的几率随机到第二项(index = 1)。
     * 似乎元素都得是整数才行，小数没试过。
     * return:
     * 数组里的第x个数
     */
    function get_weighted_rand_index(weighted_array) {
      var result_index;
      var all_weight = 0;
      weighted_array.forEach(function(item) {
        all_weight += item * 1;
      });
      var rand = Math.floor(Math.random() * all_weight);
      //跳出forEach循环的特殊写法
      try {
        var cumul = 0;
        weighted_array.forEach(function(item, index) {
          cumul += item * 1;
          // console.log('现在累计到', cumu, '====随机到的数是', ran);
          if (rand < cumul) {
            // console.log('随机数', ran, '小于', cumu, 'ok');
            result_index = index;
            // return description_array[index];
            throw new Error("terminate");
          }
        });
      } catch (e) {
        if (e.message != "terminate") throw e;
      }
      return result_index;
    }

    /*
     * 对数组加权后随机。
     * parameters:
     * description_array, 摘要数组。我也不知道干嘛用的
     * weighted_array, 权重数组
     ***** value_array, 数值数组。随机完成后会有一个数值，总会用得上的。比如对性别加权随机后结果是“男”，那“男”对应的1就从这里得出
     * text_array, 表述数组。随机完成后会有一个表述值，要生成文本给人看。比如对性别加权随机后结果是“女”，那对应的话就是“是个男孩”
     * return:
     * 数组。随机完成后把四个来源的对应位置值取出备用
     */
    function x_randomize(description_array, weighted_array, text_array) {
      var result_index = new_weight_randomize(weighted_array);
      return [description_array[result_index], text_array[result_index]];
    }

    /*
     * 把某张卡片的效果全部累加起来，
     * 即，有多个效果的循环执行之
     * parameters:
     * card, “卡片”，是一个数组，第一个元素是字符串，为卡片本身的description，
     * 第二个元素是一个数组，为卡片本身的effection
     * return:
     * 执行过程中会把该累加的效果累加完，并
     * 返回一个字符串，是补充完成后的描述
     */
    function x_count_effect(card) {
      var description = card[0];
      if (card[1].length != 0 || (card[1].length > 0 && card[1][3].length != 0)) { //有可能卡片无效果
        // 对第二个元素（权重数组）进行加权随机运算，得到一个随机序号
        var _index;
        if (card[1][1].length == 0) {
          // 如果权重那一项为空，则视为平权
          description += shuffle(card[1][2], 1);
        } else {
          //否则加权计算
          _index = new_weight_randomize(card[1][1]);
          // 计算此序号对应的效果叠加
          var eff_array = card[1][3][_index];
          eff_array.forEach(function(item, index) {
            x_eval(item, "person");
          });
          // 补充描述文字
          description += card[1][2][_index];
        }
      }
      return description;
    }

    // 显示一个文字块，并滚动屏幕到最底部（最新加的那一块）
    function add_new_log(text) {
      lay("#log .un").remove();
      lay("#log").append("<blockquote class='layui-elem-quote layui-quote-nm'>" + text + "</blockquote>").append("<div class='un'></div>");

      lay(".un")[0].scrollIntoView();
    }

    // 人生回顾界面
    function review() {
      // 上一界面【人生历程】删掉
      lay("#log").remove();
      // 开一块【人生回顾】的区域
      lay("#main").append("<div class='layui-col-xs8 layui-col-md8' id='review_area'></div>");
      // 统计、加【开局抽卡】按钮
      lay("#review_area").append("假装这里有个图").append("<blockquote class='layui-elem-quote layui-quote-nm'>" +
        "<button type='button' class='layui-btn layui-btn-lg' id='pick_cards'>再来一局</button>" +
        "</blockquote>");
      layui.$("#title").text("你的人生回顾");
      // 给【开局抽卡】按钮一个事件
      layui.$(document).on('click', "#pick_cards", pick_cards);
    }

    // 开局抽卡界面
    function pick_cards() {
      // 上一界面【人生回顾】删掉
      lay("#review_area").remove();
      // 开一块【开局抽卡】的区域
      lay("#main").append("<div class='layui-col-xs8 layui-col-md8' id='pick_cards_area'></div>");
      // 加【开局抽卡】按钮
      lay("#pick_cards_area").append("<blockquote class='layui-elem-quote layui-quote-nm'>" +
        "<button type='button' class='layui-btn layui-btn-lg' id='show_cards'>十连抽！</button>" +
        "</blockquote>");
      layui.$("#title").text("抽卡了抽卡了");
      // 给【开局抽卡】按钮一个事件
      layui.$(document).on('click', "#show_cards", show_cards);
    }

    //抽到卡十选三的界面
    function show_cards() {
      // 上一界面【开局抽卡】删掉
      lay("#pick_cards_area").remove();
      // 开一块【列出卡】的区域
      lay("#main").append("<div class='layui-col-xs8 layui-col-md8' id='show_cards_area'></div>");
      // 选卡、加【选卡完毕】按钮
      lay("#show_cards_area").append("<blockquote class='layui-elem-quote layui-quote-nm'>" +
        "<button type='button' class='layui-btn layui-btn-lg' id='set_attrs'>就这样了！</button>" +
        "</blockquote>");
      layui.$("#title").text("请选择三张卡");
      // 给【选卡完毕】按钮一个事件
      layui.$(document).on('click', "#set_attrs", set_attrs);
    }

    //选属性的界面
    function set_attrs() {
      // 上一界面【列出卡】删掉
      lay("#show_cards_area").remove();
      // 开一块【列出属性】的区域
      lay("#main").append("<div class='layui-col-xs8 layui-col-md8' id='set_attrs_area'></div>");
      // 选卡、加【正式开始】按钮
      lay("#set_attrs_area").append("<blockquote class='layui-elem-quote layui-quote-nm'>" +
        "<button type='button' class='layui-btn layui-btn-lg' id='go'>正式开始了！</button>" +
        "</blockquote>");
      layui.$("#title").text("请设定初始属性");
      // 给【正式开始】按钮一个事件
      layui.$(document).on('click', "#go", go);
    }

    // 游戏过程的界面
    function go() {
      // 上一界面【选属性】删掉
      lay("#set_attrs_area").remove();
      // 开一块【游戏进程】的区域
      lay("#main").append("<div class='layui-col-xs8 layui-col-md8' id='log'></div>");
      layui.$("#title").text("你的人生开始了");
      // 给【正式开始】按钮一个事件
      person.age = 0;
      person.isDead = 0;
      layui.$(document).on('click', '', function() {
        yearly_function();
      });
    }

    function yearly_function() {
      if (person.isDead) {
        add_new_log("你死了。");
        // 加上一个【人生回顾】按钮
        lay("#log .un").remove();
        lay("#log").append("<blockquote class='layui-elem-quote layui-quote-nm'>" +
          "<button type='button' class='layui-btn layui-btn-lg' id='review'>人生回顾</button>" +
          "</blockquote>").append("<div class='un'></div>");
        lay(".un")[0].scrollIntoView();

        // 解除绑定全局事件，同时给【人生回顾】按钮一个事件
        layui.$(document).off('click', '');

        layui.$(document).on('click', "#review", review);
      } else {
        //轮询被动事件，如满足条件则直接执行被动事件，不抽卡
        //轮询的时候，天然被动和抽卡被动放一起，然后循环。
        var all_passive_cards = original_passive_cards.concat(passive_cards);
        var all_passive_text = "";
        var all_passed_cards = [];
        if (all_passive_cards.length > 1) {
          all_passive_cards.forEach(function(item) {
            // console.log('这张卡是', item);
            var bool = true;
            item["condition"].forEach(function(i) {
              bool = bool & x_eval(i);
            });
            // console.log("这张卡的条件", bool ? "满足了！" : "不满足。");
            if (bool) {
              // console.log("当前的描述是", x_count_effect([item["description"], item["effection"]]));
              all_passive_text += x_count_effect([item["description"], item["effection"]]);
              all_passed_cards.push(item);
            }
          });
        }
        if (all_passed_cards.length > 0) {
          add_new_log(person.age + '岁：' + all_passive_text);
        } else {
          // 当该年没有被动事件的时候，遍历事件卡，找出可抽事件卡池
          var event_pool = [];

          event_cards.forEach(function(item) {
            // console.log(item["condition"]);
            var bool = true;
            item["condition"].forEach(function(i) {
              bool = bool & x_eval(i);
            });

            if (bool) {
              event_pool.push(item);
            }
            // console.log(true_event_pool);
          });

          if (event_pool.length === 0) {
            console.log("当前年份没有任何可用事件！已载入默认事件。");
            event_pool.push({
              description: "你度过了平静而又普通的一年，似乎什么都没有发生。",
              condition: [], // 无条件
              effection: [], // 无效果
              weight: 100    // 权重无所谓，因为是唯一的卡
            });
          }
          console.log("目前可抽的卡有", event_pool);

          // 对可抽事件卡池加权，得出一张卡
          var card = x_randomize(layui.$.map(event_pool, function(i, e) {
            return i['description'];
          }), layui.$.map(event_pool, function(i, e) {
            return i['weight'];
          }), function() { //嵌套数组如果只有一个值，比如[['xxx']]，则map方法会有问题
            var _ = [];
            event_pool.forEach(function(item) {
              _.push(item['effection']);
            });
            // console.log('xx');
            return _;
          }());
          console.log('你抽到了：', card);

          add_new_log(person.age + '岁：' + x_count_effect(card));
        }

        console.log("你", person.age, "岁了，你的寿元是", person.ageLimit);
        if (person.age >= person.ageLimit) {
          person.isDead = true;
        } else {
          person.age += 1;
        }
      }
    }

    layui.use(
      [
        "laydate",
        "laypage",
        "layer",
        "table",
        "carousel",
        "upload",
        "element",
      ],
      function() {
        var laydate = layui.laydate, //日期
          laypage = layui.laypage, //分页
          layer = layui.layer, //弹层
          table = layui.table, //表格
          carousel = layui.carousel, //轮播
          upload = layui.upload, //上传
          element = layui.element; //元素操作 等等...

        /*layer弹出一个示例*/
        layer.msg("游戏开始了");

        yearly_function();

        layui.$(document).on('click', '', function() {
          yearly_function();
        });

      }
    );
  </script>

  <div class="layui-row" id="main">
    <div class="layui-col-xs12 layui-col-md12">
      <div class="grid-demo grid-demo-bg1" id="title">你的人生开始了</div>
    </div>
    <div class="layui-col-xs2 layui-col-md2" id='left'>
      <div>&nbsp;</div>
    </div>
    <div class="layui-col-xs8 layui-col-md8" id="log">
      <div class="un"></div>
    </div>
  </div>

</body>

</html>